package org.duang.http.base;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.http.HttpHeaders;
import org.duang.http.auth.Signature;
import org.duang.http.common.ClientException;
import org.duang.http.common.RequestMessage;
import org.duang.http.common.ResponseMessage;
import org.duang.http.core.DefaultServiceClient;
import org.duang.http.core.DefaultServiceSSLClient;
import org.duang.http.core.HttpCallBack;
import org.duang.http.core.ServiceClient;
import org.duang.http.core.enums.HttpMethod;
import org.duang.http.core.inter.AsyncCallback;
import org.duang.http.core.inter.AsyncResult;
import org.duang.http.core.inter.ResultParser;
import org.duang.http.util.HttpUtil;
import org.duang.kit.HttpKit.ContentTypeEnum;
import org.duang.kit.ToolsKit;

public class HttpClient {

	private HttpMethod method;
	private static ServiceClient client;
	private Map<String, Object> params; // 提交的参数
	private boolean security = false;
	private InputStream content;
	private URI uri;
	private Map<String, String> headerMap; 
	private Map<String,File> fileMap;
	private String certPath;
	private String certPass;
	
	private final static Map<String, String> headers = new HashMap<String, String>();
	private static ReentrantLock clientLock = new ReentrantLock(); 
	public static final String DEFAULT_CONTENT_TYPE_VALUE = "application/json;charset=UTF-8";
	public static final String TEXTHTML_CONTENT_TYPE_VALUE = "application/x-www-form-urlencoded; text/html; charset=UTF-8";// + HttpUtil.UTF8_CHARSET
	
	private static ServiceClient newInstance() {
		if (ToolsKit.isEmpty(client)) {
			getClient();
		}
		return client;
	}
	
	private static void getClient() {
		try{
			clientLock.lock();
			if (ToolsKit.isEmpty(client)) {
				client = new DefaultServiceClient();
			}
		}catch(ClientException ce) {
			ce.getStackTrace();
			((DefaultServiceClient)client).close();
		} finally {
			clientLock.unlock();
		}
	}
	
	public HttpClient(HttpMethod method, String url, Map<String, Object> params, InputStream content, Map<String,String> headers) {
		uri = HttpUtil.getHttpURI(url);
		this.method = method;
		this.params = params;
		this.content = content;
		this.headerMap = headers;
	}
	public HttpClient(String url, InputStream content, Map<String,String> headers, String certPath, String certPass) {
		uri = HttpUtil.getHttpURI(url);
		this.content = content;
		this.headerMap = headers;
		this.certPath = certPath;
		this.certPass = certPass;
	}
	public HttpClient(HttpMethod method, String url, Map<String,String> headers, Map<String,File> fileMap) {
		uri = HttpUtil.getHttpURI(url);
		this.fileMap = fileMap;
		this.headerMap = headers;
	}

	public void setSecurity(boolean security) {
		this.security = security;
	}
	
	public void setContentType(ContentTypeEnum typeEnum) {
		if(ToolsKit.isNotEmpty(typeEnum)){
			this.headerMap.put(HttpHeaders.CONTENT_TYPE, typeEnum.getValue()+";charset="+HttpUtil.UTF8_CHARSET);
		}
	}

	public boolean isSecurity() {
		return this.security;
	}
	

	private RequestMessage buildRequestMessage() {
		headerMap = ToolsKit.isEmpty(headerMap) ? getDefaultHeaders() : headerMap;
		RequestMessage request = new RequestMessage();
		request.setMethod(method);
		request.setUrl(uri);
		
		if (security && !params.isEmpty()) {
			params.put(Signature.DUANG_SGIN_RESULT_FIELD, Signature.getSignResult(params));
		}
//		Map<String, String> headerMap = getDefaultHeaders();
		request.setHeaders(headerMap);
		
		//if(ToolsKit.isEmpty(params.get(HttpKit.TOKENID_FIELD))) params.put(HttpKit.TOKENID_FIELD, "tokenid_undefined");
		
		if(null != content){			
			request.setContent(content);			
			long length = 0l;
			try{
				length = Long.parseLong(content.available()+"");				
			} catch(Exception e){e.printStackTrace();} 
			request.setContentLength(length);
		}
		if(null != params && !params.isEmpty()) request.setParams(params);
		return request;
	}

	public HttpRes send() {
		
//		DefaultServiceClient.getHttpClient().execute(null, null);
		
		HttpCallBack<ResponseMessage> callback = new HttpCallBack<ResponseMessage>(buildResultParser(), null);
		AsyncResult<ResponseMessage> asyncResult = callback.getAsyncResult();
		HttpClient.newInstance().sendRequest(buildRequestMessage(), callback);
		if(ToolsKit.isEmpty(callback) && !asyncResult.isSuccess()) return null;
		ResponseMessage message = asyncResult.getResult();
		InputStream is = null;
		final HttpRes response = new HttpRes();	
		try {
			is = message.getContent();
			String result = ToolsKit.inputStream2String(is);
			response.setResult(result);
			response.setStatusCode(message.getStatusCode());
			response.setHeaders(message.getHeaders());
			response.setException(asyncResult.getException());
		} catch (Exception e) {
			response.setResult(asyncResult.getException().getMessage());
			response.setException(asyncResult.getException());
		}
		callback.close();
		return response;
	}

	public void send(AsyncCallback<ResponseMessage> callback) {
		HttpCallBack<ResponseMessage> cb = new HttpCallBack<ResponseMessage>(buildResultParser(), callback);
		HttpClient.newInstance().sendRequest(buildRequestMessage(), cb);
	}

	private ResultParser<ResponseMessage> buildResultParser() {
		return new ResultParser<ResponseMessage>(){
			@Override
			public ResponseMessage parse(ResponseMessage responseMessage) throws Exception {				
				return responseMessage;
			}
		};
	}
	
	public HttpRes sendSSL() {	
		try {
			String context = ToolsKit.inputStream2String(content, ToolsKit.CHARSET_UTF_8);
			return DefaultServiceSSLClient.httpSSLPost(uri.toURL().toString(), context, certPath, certPass);
		} catch (Exception e) {
			e.printStackTrace();
			HttpRes response = new HttpRes();
			response.setStatusCode(500);
			response.setException(e);
			response.setResult(e.getMessage());
			return response;
		}
	}
	
	public HttpRes upload() {
		try {
			headerMap = ToolsKit.isEmpty(headerMap) ? getDefaultHeaders() : headerMap;
			return DefaultServiceSSLClient.httpSSLPost(uri.toURL().toString(), headerMap, fileMap);
		} catch (Exception e) {
			e.printStackTrace();
			HttpRes response = new HttpRes();
			response.setStatusCode(500);
			response.setException(e);
			response.setResult(e.getMessage());
			return response;
		}
	}
	
	private Map<String, String> getDefaultHeaders() {
		if(headers.isEmpty()){			
//			headers.put(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded; text/html; charset=" + HttpUtil.UTF8_CHARSET);
			headers.put(HttpHeaders.USER_AGENT,  HttpUtil.createUserAgent());			
		}
		if(security) {
			headers.put(HttpHeaders.AUTHORIZATION, HttpUtil.createAuthorizAtionSignKey());
		}
		headers.put(HttpHeaders.DATE, System.currentTimeMillis()+"");		
		headers.put(HttpHeaders.CONTENT_TYPE, DEFAULT_CONTENT_TYPE_VALUE);
		headers.put(HttpHeaders.ACCEPT_CHARSET, HttpUtil.UTF8_CHARSET);
		return headers;
	}

	public String getCertPath() {
		return this.certPath;
	}

	public String getCertPass() {
		return this.certPass;
	}
	
	public File download(String filepath) {
        HttpCallBack<ResponseMessage> callback = new HttpCallBack<ResponseMessage>(buildResultParser(), null);
        AsyncResult<ResponseMessage> asyncResult = callback.getAsyncResult();
        client.sendRequest(buildRequestMessage(), callback);
        if (ToolsKit.isEmpty(callback) && !asyncResult.isSuccess()) return null;
        ResponseMessage message = asyncResult.getResult();
        InputStream is = message.getContent();
        File file = new File(filepath);
        file.getParentFile().mkdirs();
        FileOutputStream fileout = null;
        try {
            fileout = new FileOutputStream(file);
            byte[] buffer = new byte[10240];
            int ch = 0;
            while ((ch = is.read(buffer)) != -1) {
                fileout.write(buffer, 0, ch);
            }
            return file;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                fileout.flush();
                fileout.close();
                callback.close();
            } catch (Exception e) {
            }
        }
        return null;
    }
}
